/*
 * File:   RollServiceImplTest.cpp
 * Author: luk
 *
 * Created on Mar 1, 2015, 12:55:54 PM
 */

#include <sstream>

#include "RollServiceImplTest.h"
#include "RollServiceImpl.h"
#include "DieServiceImplTest.h"

CPPUNIT_TEST_SUITE_REGISTRATION(RollServiceImplTest);

class DieServiceMock : public DieService {
private:
    int result;

public:

    DieServiceMock(int result) {
        this->result = result;
    }

    virtual int roll(int count) {
        return result;
    }
};

RollServiceImplTest::RollServiceImplTest() {
}

RollServiceImplTest::~RollServiceImplTest() {
}

void RollServiceImplTest::setUp() {
}

void RollServiceImplTest::tearDown() {
}

void RollServiceImplTest::testSuccessRoll() {
    int TARGET = 12;

    for (int roll = 3; roll <= 18; roll++) {
        DieServiceMock dieServiceMock(roll);
        RollServiceImpl rollServiceImpl(dieServiceMock);

        SuccessRollResult result = rollServiceImpl.successRoll(TARGET);

        if (roll <= TARGET) {
            CPPUNIT_ASSERT_MESSAGE("Expected success but was failure", result.isSuccess());
        } else {
            CPPUNIT_ASSERT_MESSAGE("Expected failure but was success", !result.isSuccess());
        }
    }
}

void RollServiceImplTest::testThreeAndFourRoll() {
    for (int roll = 3; roll <= 4; roll++) {
        DieServiceMock dieServiceMock(roll);
        RollServiceImpl rollServiceImpl(dieServiceMock);

        for (int target = 1; target <= 30; target++) {
            SuccessRollResult result = rollServiceImpl.successRoll(target);
            CPPUNIT_ASSERT_MESSAGE("Expected success but was failure", result.isSuccess());
        }
    }
}

void RollServiceImplTest::testSeventeenAndEighteenRoll() {
    for (int roll = 17; roll <= 18; roll++) {
        DieServiceMock dieServiceMock(roll);
        RollServiceImpl rollServiceImpl(dieServiceMock);

        for (int target = 1; target <= 30; target++) {
            SuccessRollResult result = rollServiceImpl.successRoll(target);
            CPPUNIT_ASSERT_MESSAGE("Expected failure but was success", !result.isSuccess());
        }
    }
}

void RollServiceImplTest::testSuccessMargin() {
    DieServiceMock dieServiceMock(12);
    RollServiceImpl rollServiceImpl(dieServiceMock);
    CPPUNIT_ASSERT_EQUAL(6, rollServiceImpl.successRoll(18).getMargin());
}

void RollServiceImplTest::testFailureMargin() {
    DieServiceMock dieServiceMock(12);
    RollServiceImpl rollServiceImpl(dieServiceMock);
    CPPUNIT_ASSERT_EQUAL(3, rollServiceImpl.successRoll(9).getMargin());
}

void testCritical(int rollFrom, int rollTo, int targetFrom, int targetTo, bool(* isCritical)(SuccessRollResult)) {
    for (int roll = rollFrom; roll <= rollTo; roll++) {
        DieServiceMock dieServiceMock(roll);

        for (int target = targetFrom; target <= targetTo; target++) {
            RollServiceImpl rollServiceImpl(dieServiceMock);

            SuccessRollResult result = rollServiceImpl.successRoll(target);

            std::stringstream message;
            message << "roll: " << roll << " target: " << target;

            CPPUNIT_ASSERT_MESSAGE(message.str(), (*isCritical)(result));
        }
    }
}

bool isCriticalSuccess(SuccessRollResult result) {
    return result.isSuccess() && result.isCritical();
}

void RollServiceImplTest::testCriticalSuccess() {
    testCritical(3, 4, 1, 30, isCriticalSuccess);
    testCritical(5, 5, 15, 30, isCriticalSuccess);
    testCritical(6, 6, 16, 30, isCriticalSuccess);
}

bool isCriticalFailure(SuccessRollResult result) {
    return !result.isSuccess() && result.isCritical();
}

void RollServiceImplTest::testCriticalFailure() {
    testCritical(18, 18, 1, 30, isCriticalFailure);
    testCritical(17, 17, 1, 15, isCriticalFailure);
    testCritical(16, 16, 1, 6, isCriticalFailure);
    testCritical(15, 15, 1, 5, isCriticalFailure);
}

void RollServiceImplTest::testDamageRoll() {
    DieServiceMock dieServiceMock(7);
    RollServiceImpl rollServiceImpl(dieServiceMock);

    CPPUNIT_ASSERT_EQUAL(12, rollServiceImpl.damageRoll(2, 5));
    CPPUNIT_ASSERT_EQUAL(4, rollServiceImpl.damageRoll(2, -3));
}

